﻿package src.uipackage.ui {
	
	import src.uipackage.ui.graphics.NumberScrubberGraphic;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFieldType;
	import flash.text.TextFormat;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	
	/**
	 * UI widget that allows users to "drag" the mouse across a numeric TextField to change its value.
	 * @author Devon O.
	 */
	public class OBO_NumberScrubber extends Sprite {
		
		private var _textfield:TextField;
		private var _format:TextFormat;
		private var _button:Sprite;
		
		private var _useFractions:Boolean = false;
		private var _embedFont:Boolean = false;
		
		private var _cursor:NumberScrubberGraphic = new NumberScrubberGraphic();
		
		private var _minimum:Number;
		private var _maximum:Number;
		private var _dFault:Number;
		
		private var _textfieldwidth:Number;
		
		private var _range:Number;
		private var _current:Number;
		private var _value:Number;
		private var _stringValue:String = "";
		
		private var _centerPoint:Number;
		
		private var _mouseMoved:Boolean = false;
		private var _mousePressed:Boolean = false;
		
		/**
		 * The OBO_NumberScrubber class is a UI widget that allows users to change the value of a numeric text field by dragging their mouse to the left or right above the TextField. Clicking the TextField (without moving the mouse) allows the user to change the value directly.
		 * When the value of the TextField is changed (whether by dragging or by typing in a new number), the event Event.CHANGE is dispatched.
		 * @example The following example creates an instance of OBO_NumberScrubber and uses it to adjust the alpha property of a Sprite instance.
		 * <listing version="3.0">
		 * private function initNumberScrubber():void {
		 *		var rect:Sprite = new Sprite();
		 *		rect.name = "rect";
		 *		rect.graphics.beginFill(0x232323);
		 *		rect.graphics.drawRect(0, 0, 200, 50);
		 *		rect.graphics.endFill();
		 *
		 *		addChild(rect);
		 *	
		 *		var ns:OBO_NumberScrubber = new OBO_NumberScrubber(0, 100, 100);
		 *		ns.addEventListener(Event.CHANGE, scrubberChangeHandler);
		 *		vs.y = rect.y + rect.height + 20;
		 *		addChild(vs);
		 * }
		 * 
		 * private function scrubberChangeHandler(event:Event):void {
		 * 		event.currentTarget.parent.getChildByName("rect").alpha = event.value / 100;
		 * }
		 * </listing>
		 * 
		 * @param	The minimum value of the NumberScrubber instance
		 * @param	The maximum value of the NumberScrubber instance
		 * @param	The initial value of the NumberScrubber instance
		 * @param	The width of the NumberScrubber's TextField
		 * @param	The font to use in the NumberScrubber
		 * @param	The size of the font to use in the NumberScrubber
		 * @param	The color of the font to use in the NumberScrubber
		 * @param	Specifies whether or not the NumberScrubber font should be embedded.
		 */
		public function OBO_NumberScrubber(minimum:Number, maximum:Number, dFault:Number, width:Number = 50, font:String = "_sans", size:int = 11, color:uint = 0x000000, embed:Boolean = false):void {
			if (minimum >= maximum) {
				throw new Error("OBO_NumberScrubber Error: Minimum amount cannot be more than or the same as maximum amount.");
			}
			
			if (dFault > maximum || dFault < minimum) {
				throw new Error("OBO_NumberScrubber Error: default value must be between minimum and maximum values");
			}
			
			_minimum = minimum;
			_maximum = maximum;
			_range = maximum - minimum;
			_dFault = _value = dFault;
			_current = _dFault / _range;
			_textfieldwidth = width;
			_embedFont = embed;
			
			_format = new TextFormat(font, size, color, null, null, true);
			
			if (stage) {
				init(null);
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}
		
		private function init(event:Event):void {
			initTextField();
			initButton();
		}
		
		private function initTextField():void {
			_textfield = new TextField();
			_textfield.mouseEnabled = false;
			_textfield.selectable = false;
			_textfield.multiline = false;
			_textfield.embedFonts = _embedFont;
			_textfield.defaultTextFormat = _format;
			_textfield.restrict = "0-9.\\-";
			_textfield.type = TextFieldType.INPUT;
			_stringValue = _useFractions ? _value.toFixed(2) : String(Math.round(_value));
			setText();
			_textfield.width = _textfieldwidth;
			_textfield.height = _textfield.textHeight + 4;
			addChild(_textfield);
		}
		
		private function initButton():void {
			_button = new Sprite();
			_button.graphics.beginFill(0x00000, 0);
			_button.graphics.drawRect(0, 0, _textfield.width, _textfield.height);
			addChild(_button);
			
			_button.addEventListener(MouseEvent.MOUSE_OVER, overHandler);
			_button.addEventListener(MouseEvent.MOUSE_OUT, outHandler);
		}
		
		private function overHandler(event:MouseEvent):void {
			_cursor.x = stage.mouseX - 8;
			_cursor.y = stage.mouseY -2;
			Mouse.hide();
			stage.addChild(_cursor);
			
			stage.addEventListener(MouseEvent.MOUSE_MOVE, moveHandler);
			stage.addEventListener(Event.MOUSE_LEAVE, removeCursor);
			_button.addEventListener(MouseEvent.MOUSE_DOWN, downHandler);
		}
		
		private function outHandler(event:MouseEvent):void {
			if (!_mousePressed) {
				removeCursor(null);
			}
		}
		
		private function moveHandler(event:MouseEvent):void {
			_cursor.x = stage.mouseX - 8;
			_cursor.y = stage.mouseY - 2;
			if (_mousePressed) {
				_mouseMoved = true;
				_current += (mouseX - _centerPoint) / 5000;
				_value = _current * _range;
				
				if (_value < _minimum) _value = _minimum;
				if (_value > _maximum) _value = _maximum;
				
				_stringValue = _useFractions ? _value.toFixed(2) : String(Math.round(_value));
				
				setText();
			}
			
			event.updateAfterEvent();
		}
		
		
		
		private function downHandler(event:MouseEvent):void {
			_mousePressed = true;
			_centerPoint = mouseX;
			stage.addEventListener(MouseEvent.MOUSE_UP, removeCursor);
		}
		
		private function removeCursor(event:*):void {
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveHandler);
			stage.removeEventListener(Event.MOUSE_LEAVE, removeCursor);
			stage.removeEventListener(MouseEvent.MOUSE_UP, removeCursor);
			
			if (_mousePressed && !_mouseMoved) {
				exposeTextfield();
			}
			
			_mouseMoved = false;
			_mousePressed = false;
			
			if (stage.contains(_cursor)) {
				stage.removeChild(_cursor);
				Mouse.show();
			}
			
			_current = _value / _range;
		}
		
		private function setText():void {
			_textfield.text = _stringValue;
			dispatchEvent(new Event(Event.CHANGE));
		}
		
		private function exposeTextfield():void {
			_button.mouseEnabled = false;
			_textfield.selectable = true;
			_textfield.mouseEnabled = true;
			_textfield.background = true;
			_textfield.border = true;
			stage.focus = _textfield;
			_textfield.setSelection(0, _textfield.text.length);
			_textfield.addEventListener(FocusEvent.FOCUS_OUT, unexposeTextfield);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, checkforEnter);
		}
		
		private function checkforEnter(event:KeyboardEvent):void {
			if (event.keyCode == Keyboard.ENTER) {
				unexposeTextfield(null);
			}
		}
		
		private function unexposeTextfield(event:FocusEvent):void {
			_textfield.removeEventListener(FocusEvent.FOCUS_OUT, unexposeTextfield);
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, checkforEnter);
			
			_value = Number(_textfield.text);
			if (_value > _maximum) _value = _maximum;
			if (_value < _minimum) _value = _minimum;
			if (!_useFractions) _value = int(_value);
			_current = _value / _range;
			
			_stringValue = _useFractions ? _value.toFixed(2) : String(Math.round(_value));
			
			setText();
			
			_button.mouseEnabled = true;
			_textfield.selectable = false;
			_textfield.mouseEnabled = false;
			_textfield.background = false;
			_textfield.border = false;
		}
		
		
		// public
		
		/**
		 * Specifies whether or not the NumberScrubber instance should display fractional values.
		 */
		public function get useFractions():Boolean { return _useFractions; }
		
		public function set useFractions(value:Boolean):void {
			_useFractions = value;
		}
		
		/**
		 * Gets or sets the current numeric value of the NumberScrubber instance
		 */
		public function get value():Number {
			if (!_useFractions) return Math.round(_value);
			return _value; 
		}
		
		public function set value(value:Number):void {
			if (value >= _minimum && value <= _maximum) {
				if (!_useFractions) value = int(value);
				_value = value;
				setText();
			}
		}
		
		/**
		 * Specifies whether or not the NumberScrubber's font should be embedded
		 */
		public function get embedFont():Boolean { return _embedFont; }
		
		public function set embedFont(value:Boolean):void {
			_embedFont = value;
			if (_textfield) _textfield.embedFonts = _embedFont;
		}
	}
}